#ifndef CONTROLS_H
#define CONTROLS_H

#include <iostream>
#include "player.h"
#include "map.h"
#include "tileNode.h"
#include "game.h"

using namespace std;

class controls
{
		
	public:
		
		controls(player* hero, map* mapOne,game* gamez);
		~controls();
		
		void initKeys();
		void upPressed();
		void downPressed();
		void leftPressed();
		void rightPressed();
		bool npcClick();
		void attackNpc(int npcNum);
		void setPrint();
		void upUnPressed();
		void downUnPressed();
		void leftUnPressed();
		void rightUnPressed();
		void escUnPressed();
		
		void moveEvents();
		void checkUp(ALLEGRO_EVENT event);
		void checkDown(ALLEGRO_EVENT event);
		
		void masterChecks(ALLEGRO_EVENT event);
		
		void redrawFalse();
		bool isExit();
		void allegroEvent();
		bool redraw;
		
		void setMouseX(int x);
		void setMouseY(int y);
		void mouseClick(ALLEGRO_EVENT event);
		void runToClickX();
		void runToClickY();
		void updatePlayerPos();
		void drawSpot();
		
		game* gamez;
		player* dude;
		map* mapOne;
		
		void aStar();
		vector<tileNode*> surrounding(tileNode* node);
		tileNode* putIntoClosed(tileNode* current);
		void clickAdjustment();
        void findG(int num, tileNode* node);
        void findH(tileNode* node);
        void findF(tileNode* node);
		bool checkClosed(int x, int y);
		bool checkVector(int x, int y);
		void putIntoTemptVector(int x, int y, vector<tileNode*> temp, int num);	
		void getAdjacent(tileNode* currentNode);
		bool solid(int x, int y);
		void moveParent(tileNode* node, tileNode* current, int num);
		tileNode* findNode(int x, int y);
		void backtrack();
		void clearStack();

		bool key[5];
		enum THE_KEYS{KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_ESC};
			

		vector<tileNode *> closed;
		vector<tileNode *> open;
		stack<tileNode *> pathStack;
		int mouse_x;
		int mouse_y;
		bool exit;
		bool mouseMove;
	private:
};

//Constructor
 controls::controls(player* hero, map* mapOne, game* gamez){
	initKeys();
	dude = hero;
	exit = false;
	redraw = true;
	mouseMove = false;
	this->mapOne = mapOne;
	this->gamez=gamez;
}

//Destructor
 controls::~controls(){
}

void controls::initKeys(){
	for(int i = 0; i < 5; i++)
	{
		key[i] = false;
	}
}

//The up key is pressed
void controls::upPressed(){
	key[KEY_UP]=true;
}

//the down key is pressed
void controls::downPressed(){
	key[KEY_DOWN]=true;
}

//the left key is pressed
void controls::leftPressed(){
	key[KEY_LEFT]=true;
}

//the right key is pressed
void controls::rightPressed(){
	key[KEY_RIGHT]=true;
}

//the up key has been let go
void controls::upUnPressed(){
	key[KEY_UP]=false;
}

//the down key has been let go
void controls::downUnPressed(){
	key[KEY_DOWN]=false;
}

//the left key has been let go
void controls::leftUnPressed(){
	key[KEY_LEFT]=false;
}

//the right key has been let go
void controls::rightUnPressed(){
	key[KEY_RIGHT]=false;
}

//the excape key has been let go
void controls::escUnPressed(){
	key[KEY_ESC]=false;
}

//Checks all of the input sources and will call functions according to 
//their use.
void controls::masterChecks(ALLEGRO_EVENT event){
	if(gamez->state==1){
	if(event.type == ALLEGRO_EVENT_TIMER)
		moveEvents();
	else if(event.type == ALLEGRO_EVENT_KEY_UP)
		checkUp(event);
	else if(event.type == ALLEGRO_EVENT_KEY_DOWN)
		checkDown(event);
	}
	if(event.type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN){
	 if(event.mouse.button & 1)
	 {
		mouseClick(event);
	
	 }
	}
}

//A switch statement thatchecks to see if the keys have been released. 
//If they are a function will be called to change the bool value.
void controls::checkUp(ALLEGRO_EVENT event){
		switch(event.keyboard.keycode) 
		{
			case ALLEGRO_KEY_ESCAPE:
				exit = true;
				break;
				
			case ALLEGRO_KEY_UP:
				upUnPressed();
				break;
				
			case ALLEGRO_KEY_DOWN:
				downUnPressed();
				break;
				
			case ALLEGRO_KEY_LEFT:
				leftUnPressed();
				break;
				
			case ALLEGRO_KEY_RIGHT:
				rightUnPressed();
				break;
		}	
}

//A switch statement thatchecks to see if the keys have been pressed down. 
//If they are a function will be called to change the bool value.
void controls::checkDown(ALLEGRO_EVENT event){
   
	 switch(event.keyboard.keycode)
	{
	 	case ALLEGRO_KEY_UP:
	 		upPressed();
	 		break;
	 		
	 	case ALLEGRO_KEY_DOWN:
			downPressed();
			break;
			
		case ALLEGRO_KEY_LEFT:
			leftPressed();
			break;
			
		case ALLEGRO_KEY_RIGHT:
			rightPressed();
			break;
	}
	mouseMove = false;
}

//Returns the exit bool value.
bool controls::isExit(){
	return exit;
}

//Gets bool values from multiple bool variables of different keys and 
//calls the corresponding functions. 
void controls::moveEvents(){
		redraw = true;
		if(key[KEY_UP])
			dude->movePlayerUp();
		if(key[KEY_DOWN])
			dude->movePlayerDown();
		if(key[KEY_RIGHT])
			dude->movePlayerRight();
		if(key[KEY_LEFT])
			dude->movePlayerLeft();
		if(mouseMove)
		{
			updatePlayerPos();
		}
}

//Makes the redraw bool value false.
void controls::redrawFalse()
{
	redraw = false;
}

//Initializes the ALLEGRO_EVENT class
void controls::allegroEvent(){
	ALLEGRO_EVENT ev;
}

//Sets the position of the mouse click in the x position. The camera is added
//because of the screen scrolling.
void controls::setMouseX(int x){
	mouse_x = (((x + mapOne->camera_x)/16)*16);
}
//Sets the position of the mouse click in the y position. The camera is added
//because of the screen scrolling.
void controls::setMouseY(int y){
	mouse_y = (((y + mapOne->camera_y )/16)*16);
}
//Gets the coordinates x and y from the mouse click event and passes them into
//the functions setMouseX and setMouseY to record those positions.
void controls::mouseClick(ALLEGRO_EVENT event){
		
		
	if(gamez->state==0&&event.mouse.x>289&&event.mouse.y<298&&event.mouse.x<592&&event.mouse.y>195)
		gamez->changeStatePlaying();
	else if(gamez->state==1){
		setMouseX(event.mouse.x);
		setMouseY(event.mouse.y);
	if(npcClick())
		setPrint();
	else if(mouse_x<=mapOne->mapWidth&&mouse_x>=0&&mouse_y<=mapOne->mapHeight&&mouse_y>=0&&(mouse_y!=dude->y||mouse_x!=dude->x)){
		if(!mapOne->tileVector[mapOne->tile2DVector[mouse_x/16][mouse_y/16]]->solid){
		mouseMove=true;
		clearStack();
		clickAdjustment();
		aStar();
		}
	}
	}
}

//attacks the npc using the item's damage value
void controls::attackNpc(int npcNum){
	gamez->gameNpcs[npcNum]->hurt(dude->itemz[0]->getDamage());
}

//does nothing yet
//will print out actions by the player
void controls::setPrint(){
	
}

//Checks whether or not the player has clicked on a non playable character
bool controls::npcClick(){
	int size = gamez->gameNpcs.size();
	for(int i = 0; i< size; i++){
		if(gamez->gameNpcs[i]->live&&mouse_x>=gamez->gameNpcs[i]->x&&mouse_x<=gamez->gameNpcs[i]->x+gamez->gameNpcs[i]->width&&mouse_y>=gamez->gameNpcs[i]->y&&mouse_y<=gamez->gameNpcs[i]->y+gamez->gameNpcs[i]->height){
			attackNpc(i);
			return true;
		}
	}	
	return false;	
}

//Moves the player to the x position of a mouse  click
void controls::runToClickX(){
	if(dude->x < pathStack.top()->x)
		dude->movePlayerRight();
	else if(dude->x > pathStack.top()->x)
		dude->movePlayerLeft();
}

//Moves the player to the y position of a mouse click
void controls::runToClickY(){
	if(dude->y < pathStack.top()->y)
		dude->movePlayerDown();
	else if(dude->y > pathStack.top()->y)
		dude->movePlayerUp();	
}

//Updates the position  x and y axis of the player until he has made it 
//to the position that the mouse click has set.
void controls::updatePlayerPos(){
	if(!pathStack.empty()){
	if((dude->x <= mouse_x + 1 && dude->x >= mouse_x - 1) && (dude->y <= mouse_y + 1 && dude->y >= mouse_y - 1)){
		mouseMove = false;
		clearStack();
	}
	else if(pathStack.top()->y!=dude->y||pathStack.top()->x!=dude->x){
			runToClickX();
			runToClickY();
	}
	else pathStack.pop();
	}
}

//Draws a circle where the player has clicked with the mouse.
void controls::drawSpot(){
	if(mouseMove&&mouse_x<=mapOne->mapWidth&&mouse_x>=0&&mouse_y<=mapOne->mapHeight&&mouse_y>=0){
		if(!mapOne->tileVector[mapOne->tile2DVector[mouse_x/16][mouse_y/16]]->solid)
			al_draw_filled_circle(mouse_x + (dude->width/2), mouse_y + dude->height, dude->width/2, al_map_rgb(255,255,255));
	}
}

//The aStar algorithm calculates a that the player will take to a given 
//goal. In the player's case this is the mouse_x and mouse_y variables
void controls::aStar(){
	tileNode* newNode= new tileNode((dude->x/16)*16,(dude->y/16)*16);
	tileNode* currentNode = newNode;
	currentNode->parent = NULL;
	currentNode->G=0;
	findH(currentNode);
	findF(currentNode);
	open.push_back(currentNode);
	
	while(!open.empty()){
		if(currentNode->x==mouse_x&&currentNode->y==mouse_y)
			break;
		currentNode = putIntoClosed(currentNode);
		getAdjacent(currentNode);
	}
	backtrack();
	open.clear();
	closed.clear();
}

//Checks all of the surrounding tiles of the currentNode. If they are not
//solid, they will be pushed into the open vector
void controls::getAdjacent(tileNode* currentNode){
	tileNode* node;
	int x=currentNode->x;
	int y=currentNode->y;
	
	//Up
		y-=16;
	if(solid(x, y)==false){
		
		if(checkClosed(x,y)==true&&checkVector(x,y)==true){
			//cout<<" ("<<x<<","<<y<<") ";
		node = new tileNode(x,y);
		node->parent=currentNode;
		findG(16,node);
		findH(node);
		findF(node);
		open.push_back(node);
		}
		else if(checkVector(x,y)==false){
			node = findNode(x, y);
			moveParent(node, currentNode, 16);
		}
	}
	//Diagonal Right Up
		x+=16;
	if(solid(x,y)==false){
		
		if(checkClosed(x,y)==true&&checkVector(x,y)==true){
			//cout<<" ("<<x<<","<<y<<") ";
		node = new tileNode(x,y);
		node->parent=currentNode;
		findG(22,node);
		findH(node);
		findF(node);
		open.push_back(node);
		}
		else if(checkVector(x,y)==false){
			node = findNode(x, y);
			moveParent(node, currentNode, 22);
		}
	}
	//Right
		y+=16;
	if(solid(x,y)==false){
		
		if(checkClosed(x,y)==true&&checkVector(x,y)==true){
			//cout<<" ("<<x<<","<<y<<") ";
		node = new tileNode(x,y);
		node->parent=currentNode;
		findG(16,node);
		findH(node);
		findF(node);
		open.push_back(node);
		}
		else if(checkVector(x,y)==false){
			node = findNode(x, y);
			moveParent(node, currentNode, 16);
		}
	}
	//Diagonal Right Down
		y+=16;
	if(solid(x,y)==false){
		
		if(checkClosed(x,y)==true&&checkVector(x,y)==true){
			//cout<<" ("<<x<<","<<y<<") ";
		node = new tileNode(x,y);
		node->parent=currentNode;
		findG(22,node);
		findH(node);
		findF(node);
		open.push_back(node);
		}
		else if(checkVector(x,y)==false){
			node = findNode(x, y);
			moveParent(node, currentNode, 22);
		}
	}
	//Down
		x-=16;
	if(solid(x,y)==false){
		
		if(checkClosed(x,y)==true&&checkVector(x,y)==true){
			//cout<<" ("<<x<<","<<y<<") ";
		node = new tileNode(x,y);
		node->parent=currentNode;
		findG(16,node);
		findH(node);
		findF(node);
		open.push_back(node);
		}
		else if(checkVector(x,y)==false){
			node = findNode(x, y);
			moveParent(node, currentNode, 16);
		}
	}
	//Diagonal Left Down
		x-=16;
	if(solid(x,y)==false){
	
		if(checkClosed(x,y)==true&&checkVector(x,y)==true){
			//cout<<" ("<<x<<","<<y<<") ";
		node = new tileNode(x,y);
		node->parent=currentNode;
		findG(22,node);
		findH(node);
		findF(node);
		open.push_back(node);
		}
		else if(checkVector(x,y)==false){
			node = findNode(x, y);
			moveParent(node, currentNode, 22);
		}
	}
	//Left
		y-=16;
	if(solid(x,y)==false){
		;
		if(checkClosed(x,y)==true&&checkVector(x,y)==true){
			//cout<<" ("<<x<<","<<y<<") ";
		node = new tileNode(x,y);
		node->parent=currentNode;
		findG(16,node);
		findH(node);
		findF(node);
		open.push_back(node);
		}
		else if(checkVector(x,y)==false){
			node = findNode(x, y);
			moveParent(node, currentNode, 16);
		}
	}
	//Diagonal Left Up
		y-=16;
	if(solid(x,y)==false){
		
		if(checkClosed(x,y)==true&&checkVector(x,y)==true){
			//cout<<" ("<<x<<","<<y<<") ";
		node = new tileNode(x,y);
		node->parent=currentNode;
		findG(22,node);
		findH(node);
		findF(node);
		open.push_back(node);
		}
		else if(checkVector(x,y)==false){
			node = findNode(x, y);
			moveParent(node, currentNode, 22);
		}
	}
}

//checks to see if there is a tileNode with the bool value solid at the
//given x and y coordinates
bool controls::solid(int x, int y){
	if(!mapOne->tileVector[mapOne->tile2DVector[x/16][y/16]]->solid&&!mapOne->tileVector[mapOne->tile2DVector[(x+(dude->width/2))/16][y/16]]->solid&&!mapOne->tileVector[mapOne->tile2DVector[x/16][(y+(dude->height/2))/16]]->solid&&!mapOne->tileVector[mapOne->tile2DVector[(x+(dude->width/2))/16][(y+(dude->height/2))/16]]->solid){
		return false;
	}
	else return true;
}

//Adjusts where a person has cicked in case part of the sprite will 
//be inside the solid tile
void controls::clickAdjustment(){
	if(mapOne->tileVector[mapOne->tile2DVector[mouse_x/16][(mouse_y+16)/16]]->solid)
		mouse_y-=16;
	if(mapOne->tileVector[mapOne->tile2DVector[(mouse_x+16)/16][mouse_y/16]]->solid)
		mouse_x-=16;
}

//Changes a parent node for a 
void controls::moveParent(tileNode* node, tileNode* current, int num){
	if(current->G+num<node->G){
		node->G=current->G+num;
		node->parent=current;
		findF(node);
	}
}

tileNode* controls::findNode(int x, int y){
	for(int i = 0; i < open.size(); i++){
		if(x == open[i]->x&&y==open[i]->y)
			return open[i];
	}
}

tileNode* controls::putIntoClosed(tileNode* current){
	tileNode* node = NULL;
	if(!open.empty()){
	node=open[0];
	int num=0; 
	for(int i = 0; i<open.size(); i++){
		if(open[i]->F < node->F){
			node=open[i];
			num=i;
		}
	}	
	    closed.push_back(node);	
		open.erase(open.begin()+num);
		//cout<<" ("<<node->x <<","<<node->y<<") "<<" F:"<<node->F<<" G:"<<node->G<<" H:"<<node->H;
		return node;
	}	
}

void controls::findG(int num, tileNode* node){
    node->G=node->parent->G+num;
}

void controls::findH(tileNode* node){
	if(node->x<=mouse_x)
		node->H=mouse_x-node->x;
	else node->H=node->x-mouse_x;
	if(node->y<=mouse_y)
		node->H=node->H+(mouse_y-node->y);
	else node->H=node->H+(node->y-mouse_y);
}

void controls::findF(tileNode* node){
    node->F=node->G+node->H;
}

bool controls::checkClosed(int x, int y){
	for(int i = 0; i < closed.size(); i++){
		if(closed[i]->x==x&&closed[i]->y==y)
		return false;
	}
	return true;
}

bool controls::checkVector(int x, int y){
	for(int i = 0; i < open.size(); i++){
		if(open[i]->x==x&&open[i]->y==y)
		return false;
	}
	return true;
}

void controls::putIntoTemptVector(int x, int y, vector<tileNode*> temp, int num){
	for(int i = 0; i < open.size(); i++){
		if(open[i]->x==x&&open[i]->y==y){
			temp.push_back(open[i]);
			temp.back()->parent=closed.back();
			findG(num,temp.back());
			findF(temp.back());
			break;
		}
	}
}

void controls::backtrack()
{
	tileNode* node;
	node = closed.back();
	pathStack.push(node);
	while(node->parent!=NULL){
		node=node->parent;
		pathStack.push(node);
	}
}

void controls::clearStack(){
	while(!pathStack.empty()){
		pathStack.pop();
	}
}
#endif

